<template>
  <div ref="canvas"></div>
</template>

<script setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import Stats from "three/examples/jsm/libs/stats.module";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import { ref, onMounted } from "vue";
import {
  addBasicMaterialSettings,
  applyMeshNormalMaterial,
  applyMeshStandardMaterial,
  redrawGeometryAndUpdateUI
} from "@/book/ThreeUtils";

// 画布
const canvas = ref();
// 场景
const scene = new THREE.Scene();
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// 相机
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(-30, 10, 30);
camera.lookAt(0, 0, 0);
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);
// 性能统计
const stat = Stats();
// plane
const planeGeometry = new THREE.PlaneGeometry(1000, 1000);
const planeMaterial = new THREE.MeshPhongMaterial({
  color: 0xffffff
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotateX(- Math.PI / 2);
plane.position.set(0, -30, 0);

scene.add(plane);
// spot light
const spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-10, 30, 40);
spotLight.shadow.mapSize.width = window.innerWidth;
spotLight.shadow.mapSize.height = window.innerHeight;
spotLight.castShadow = true;
spotLight.shadow.camera.fov = 15;
spotLight.decay = 2;

scene.add(spotLight);

const ambientLight = new THREE.AmbientLight(0x343434);
ambientLight.castShadow = true;
scene.add(ambientLight);
// shape

// gui
const controls = {
  appliedMaterial: applyMeshNormalMaterial,
  curveSegments: 12, // 分段数
  castShadow: true, // 开启阴影
  planeVisible: true, // 是否展示 plane
  redraw: () => {
    redrawGeometryAndUpdateUI(gui, scene, controls, () => {
      return new THREE.ShapeGeometry(drawShape(), controls.curveSegments).center();
    })
  },
};
const gui = new GUI();
gui.add(controls, 'curveSegments', 1, 100, 1).onChange(controls.redraw);
gui.add(controls, 'appliedMaterial', {
  meshNormal: applyMeshNormalMaterial,
  meshStandard: applyMeshStandardMaterial
}).onChange(controls.redraw);
gui.add(controls, 'castShadow').onChange((e) => {
  controls.mesh.castShadow = e;
});
gui.add(controls, 'planeVisible').onChange((e) => {
  plane.material.visible = e;
});
controls.redraw();

let step = 0;
const render = () => {
  stat.update();
  orbitControl.update();

  step += 0.002;

  controls.mesh.rotation.y = step
  controls.mesh.rotation.x = step
  controls.mesh.rotation.z = step

  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

onMounted(() => {
  canvas.value.appendChild(stat.domElement);
  canvas.value.appendChild(renderer.domElement);

  render();
});

function drawShape() {
  const shape = new THREE.Shape();

  // 起点
  shape.moveTo(10, 10);
  // 起始线段
  shape.lineTo(10, 40);
  // 图的顶部, 向右弯曲 - 前四个分别是两个控制点的x,y坐标，最后两个是终止点的x,y坐标
  shape.bezierCurveTo(15, 25, 25, 25, 30, 40);

  // 将二维点的数组以曲线连接起来，形成一条平滑曲线。
  shape.splineThru([
    new THREE.Vector2(32, 30),
    new THREE.Vector2(28, 20),
    new THREE.Vector2(30, 10),
  ]);

  shape.quadraticCurveTo(20, 15, 10, 10);

  const rightEye = new THREE.Path();
  rightEye.absellipse(16, 24, 2, 3, 0, Math.PI * 2, true);
  shape.holes.push(rightEye);

  const leftEye = new THREE.Path();
  leftEye.absellipse(23, 24, 2, 3, 0, Math.PI * 2, true);
  shape.holes.push(leftEye);

  const mouth = new THREE.Path();
  mouth.absarc(20, 16, 2, 0, Math.PI, true);
  shape.holes.push(mouth);

  return shape;
}

</script>
